﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Threading;
using System.Threading;
using System.Drawing;
using System.Diagnostics;
using System.Data.SqlTypes;
using System.Windows.Media.Imaging;
using System.Data;
using System.Runtime.Serialization.Formatters.Binary;
using storeProcedure;

namespace Player
{
    public class UnixSession : SessionBase
    {
        public enum Type : int
        {
            Unknown = 0,
            ResizeWindow = 1,
            StandardIn = 2,
            StandardOut = 3,
            StandardError = 4,
        }

        private string m_connectionString;
        private long m_start;
        private long m_end;
        private RenderEngineBase m_renderEngine;

        public UnixSession(string connectionString, Guid guid)
        {
            m_connectionString = connectionString;
            SessionId = guid;

            UnixSessionGetHeader.Output output = UnixSessionGetHeader.ExecuteNonQuery(m_connectionString,
                     new UnixSessionGetHeader.Input(SessionId, DatabaseHelper.AuditStoreDatabaseIds[0]));
            UserName = output.User;
            MachineName = output.Machine;
            UtcStartTime = output.StartTime;

            var firstFrameInput = new UnixSessionGetFirstFrame.Input(SessionId, DatabaseHelper.AuditStoreDatabaseIds[0]);
            var firstFrame = UnixSessionGetFirstFrame.Execute(m_connectionString, firstFrameInput, CommandBehavior.SequentialAccess).LastOrDefault();
            StartTime = firstFrame.CapturedTime.ToLocalTime();
            m_start = firstFrame.TimeStamp;

            // Get the last frame of Unix session
            var lastFrameInput = new UnixSessionGetLastFrame.Input(SessionId, DatabaseHelper.AuditStoreDatabaseIds[0]);

            var lastFrame = UnixSessionGetLastFrame.Execute(m_connectionString, lastFrameInput, CommandBehavior.SequentialAccess).LastOrDefault();
            m_end = lastFrame.TimeStamp;
            EndTime = lastFrame.CapturedTime.ToLocalTime();

            Engine = CreateRenderEngine();

            Engine.ImageSourceUpdated += new EventHandler(en_ImageSourceUpdated);
        }

        void en_ImageSourceUpdated(object sender, EventArgs e)
        {
            Image = Engine.Image;
        }

        public override RenderEngineBase CreateRenderEngine()
        {
            m_renderEngine = new UnixRenderEngine();
            return m_renderEngine;
        }

        public override IEnumerable<FrameBase> LoadFrames(long startTimeStamp)
        {
            //Log.Verbose("Timestamp: {0}", startTimeStamp);

            // Unix session is very sensitive to the timestamp.
            // T-SQL will do some conversion on the timestamp.
            // If the timestamp is too small or too large, the conversion in T-SQL may throw exception
            if (startTimeStamp < Start) startTimeStamp = Start;
            if (startTimeStamp > End) startTimeStamp = End;

            var auditDatabaseIds = DatabaseHelper.AuditStoreDatabaseIds;

            int i;
            bool foundSnapshot = false;
            for (i = 0; i < auditDatabaseIds.Count; i++)
            {
                int databaseId = auditDatabaseIds[i];
                UnixSessionGetSnapshot.Input input1 = new UnixSessionGetSnapshot.Input(SessionId, databaseId, startTimeStamp);
                foreach (UnixSessionGetSnapshot.Output output1 in UnixSessionGetSnapshot.Execute(m_connectionString, input1))
                {
                    startTimeStamp = output1.TimeStamp;
                    foundSnapshot = true;
                    break;
                }
                if (foundSnapshot) break;
            }

            if (!foundSnapshot)
            {
                Log.Warning("No snapshot found.  Unix session replay may not be correct.");
                i = 0;
            }

            for (int j = i; j < auditDatabaseIds.Count; j++)
            {
                int databaseId = auditDatabaseIds[j];
                UnixSessionGetFrames.Input input = new UnixSessionGetFrames.Input(SessionId, databaseId, startTimeStamp);
                foreach (UnixSessionGetFrames.Output output in UnixSessionGetFrames.Execute(m_connectionString, input, CommandBehavior.SequentialAccess))
                {
                    if (output.TimeStamp > End)
                    {
                        break;
                    }

                    FrameBase p = new UnixFrame(output.Sequence,
                                          output.CapturedTime,
                                          output.TimeStamp,
                                          output.ScreenWidth,
                                          output.ScreenHeight,
                                          output.Data,
                                          (int)output.Type);

                    yield return p;
                }
            }
        }

        public override IEnumerable<FrameBase> SearchFrames(long startTimeStamp, string[] searchWords)
        {
            //Log.Verbose("Timestamp: {0}", startTimeStamp);
            //Log.Verbose("Search words:");
            //foreach (string word in searchWords)
            //{
            //    Log.Verbose(word);
            //}

            //MemoryStream stream = new MemoryStream();
            //BinaryFormatter formatter = new BinaryFormatter();
            //formatter.Serialize(stream, searchWords);

            //var auditDatabaseIds = DatabaseHelper.GetDatabaseIdsForUnixSession(null, null, null);
            //foreach (var databaseId in auditDatabaseIds)
            //{
            //    UnixSessionSearchFramesByKeywords.Input input = new UnixSessionSearchFramesByKeywords.Input(SessionId, startTimeStamp, stream.GetBuffer(), databaseId);
            //    foreach (UnixSessionSearchFramesByKeywords.Output output in UnixSessionSearchFramesByKeywords.Execute(m_connectionString, input))
            //    {
            //        if (output.TimeStamp > End)
            //        {
            //            break;
            //        }

            //        Frame p = new Frame(output.Sequence,
            //                              output.CapturedTime,
            //                              output.TimeStamp,
            //                              output.ScreenWidth,
            //                              output.ScreenHeight,
            //                              output.Data,
            //                              (int)output.Type);

            //        yield return p;
            //    }
            //}
            return null;
        }

        public override IEnumerable<EventItem> LoadEvents()
        {
            throw new NotImplementedException();
        }

        public override long Start
        {
            get { return m_start; }
        }

        public override long End
        {
            get { return m_end; }
        }
    }
}
